package org.jboss.seam.ioc.spring; import org.jboss.seam.ScopeType; import org.jboss.seam.core.Init; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; import org.springframework.beans.factory.xml.BeanDefinitionDecorator; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.core.Ordered; import org.springframework.util.ClassUtils; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * NamespaceHandler for a number of seam features in spring. * * @author youngm */ public class SeamNamespaceHandler extends NamespaceHandlerSupport { public static final String SEAM_SCOPE_POST_PROCESSOR = "org.jboss.seam.ioc.spring.SeamScopePostProcessor"; public static final String SEAM_COMPONENT_POST_PROCESSOR = "org.jboss.seam.ioc.spring.SeamComponentPostProcessor"; public static final String SEAM_COMPONENT_POST_PROCESSOR_BEAN_NAME = "org.jboss.seam.ioc.spring.seamComponentPostProcessor"; /** * @see org.springframework.beans.factory.xml.NamespaceHandler#init() */ public void init() { registerBeanDefinitionParser("configure-scopes", new SeamConfigureScopeParser()); registerBeanDefinitionParser("instance", new SeamInstanceBeanDefinitionParser()); registerBeanDefinitionDecorator("component", new SeamComponentBeanDefinitionDecorator()); } /** * Registers the SeamScopePostProcessor in this bean factory under the name * defined in SEAM_SCOPE_POST_PROCESSOR. <seam:configure-scope/> * * @see SeamScopePostProcessor * @author youngm */ private static class SeamConfigureScopeParser extends AbstractSimpleBeanDefinitionParser { /** * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element) */ @Override protected Class getBeanClass(Element element) { return SeamScopePostProcessor.class; } /** * @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, * org.springframework.beans.factory.support.AbstractBeanDefinition, * org.springframework.beans.factory.xml.ParserContext) */ @Override protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { return SEAM_SCOPE_POST_PROCESSOR; } } /** * Makes a SeamFactoryBean available for use in a spring ApplicationContext. * <seam:instance name="someSeamComponent"/> * * @see SeamFactoryBean * @author youngm */ private static class SeamInstanceBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { return SeamFactoryBean.class; } } /** * Makes an existing spring bean definition a seam component or provides * hints in the creation of a seam component. Will use the bean definitions * name and class by default and the classes annotatated InterceptionType. * * If proxy=true will wrap the spring bean in a cglib proxy for safe * injection into singletons. * * <seam:component/> * * @author youngm */ private static class SeamComponentBeanDefinitionDecorator implements BeanDefinitionDecorator { private static final String INTERCEPT_TYPE_ATTR = "intercept"; private static final String SPRING_NAME_ATTR = "spring-name"; private static final String SEAM_NAME_ATTR = "name"; private static final String BEAN_CLASS_ATTR = "class"; private static final String AUTO_CREATE_ATTR = "auto-create"; /** * @see org.springframework.beans.factory.xml.BeanDefinitionDecorator#decorate(org.w3c.dom.Node, * org.springframework.beans.factory.config.BeanDefinitionHolder, * org.springframework.beans.factory.xml.ParserContext) */ public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) { // Add the Seam Component Post Processor to the bean factory if it // doesn't already exist if (!parserContext.getRegistry().containsBeanDefinition( SEAM_COMPONENT_POST_PROCESSOR_BEAN_NAME)) { Class cls; try { cls = ClassUtils.forName(SEAM_COMPONENT_POST_PROCESSOR); } catch (ClassNotFoundException e) { throw new IllegalStateException("Unable to load class '" + SEAM_COMPONENT_POST_PROCESSOR + "' make sure you have the jboss-seam-spring.jar in your classpath.", e); } RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); beanDefinition.getPropertyValues().addPropertyValue("order", Ordered.LOWEST_PRECEDENCE); parserContext.getRegistry().registerBeanDefinition( SEAM_COMPONENT_POST_PROCESSOR_BEAN_NAME, beanDefinition); } // get the optional beanClass String beanClassName = definition.getBeanDefinition().getBeanClassName(); if (node.getAttributes().getNamedItem(BEAN_CLASS_ATTR) != null) { beanClassName = node.getAttributes().getNamedItem(BEAN_CLASS_ATTR).getNodeValue(); } String beanName = definition.getBeanName(); // get the name of the seam component to create String seamName = beanName; if (node.getAttributes().getNamedItem(SEAM_NAME_ATTR) != null) { seamName = node.getAttributes().getNamedItem(SEAM_NAME_ATTR).getNodeValue(); } // get the name of the spring bean to use String springName = beanName; if (node.getAttributes().getNamedItem(SPRING_NAME_ATTR) != null) { springName = node.getAttributes().getNamedItem(SPRING_NAME_ATTR).getNodeValue(); } // get the interception type to use Boolean interceptionType = null; if (node.getAttributes().getNamedItem(INTERCEPT_TYPE_ATTR) != null) { interceptionType = Boolean.valueOf(node.getAttributes().getNamedItem( INTERCEPT_TYPE_ATTR).getNodeValue()); } // get the requested scope ScopeType scope = ScopeType.valueOf(node.getAttributes().getNamedItem("scope") .getNodeValue()); if (scope != ScopeType.STATELESS && !BeanDefinition.SCOPE_PROTOTYPE.equals(definition.getBeanDefinition() .getScope())) { throw new IllegalStateException( "The spring bean scope must be prototype to use a seam scope other than STATELESS."); } if (!(parserContext.getRegistry() instanceof BeanFactory)) { throw new RuntimeException("For some reason your registry is not a BeanFactory"); } SpringComponent.addSpringComponent(seamName, springName, beanClassName, scope, (BeanFactory) parserContext.getRegistry(), interceptionType); if (node.getAttributes().getNamedItem(AUTO_CREATE_ATTR) != null) { if (Boolean.valueOf(node.getAttributes().getNamedItem(AUTO_CREATE_ATTR).getNodeValue())) { Init.instance().addAutocreateVariable(seamName); } } return definition; } } }